package com.peng.rpc.common.client;

import com.peng.rpc.entity.RpcRequest;
import com.peng.rpc.entity.RpcResponse;
import com.peng.rpc.netty.client.NettyRpcClient;
import com.peng.rpc.socket.client.SocketRpcClient;
import com.peng.rpc.util.ResponseChecker;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
 * 客户端无法真正执行方法，只能创建一个代理，由这个代理去利用sendRequest()调用远程方法
 * 当前这个类就是为指定的客户端对象创建代理
 * invoke()方法是代理对象的实际调用，这里没有任何的原方法的调用，只是单纯传递方法名和方法参数给远程对象。
 * @author Wonderson
 * @email wpp.ds@foxmail.com
 * @date 2022/2/27 19:41
 */
@Slf4j
public class RpcClientProxy implements InvocationHandler {


    private final RpcClient rpcClient;


    public RpcClientProxy(RpcClient rpcClient) {
        this.rpcClient = rpcClient;
    }

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<?> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("通过代理调用远程方法：{}#{}", method.getDeclaringClass().getCanonicalName(), method.getName());
        RpcRequest rpcRequest = new RpcRequest(UUID.randomUUID().toString(), method.getDeclaringClass().getName(),
                method.getName(), args, method.getParameterTypes(), false);
        RpcResponse rpcResponse = null;
        if (rpcClient instanceof NettyRpcClient) {
            CompletableFuture<RpcResponse> responseFuture = (CompletableFuture<RpcResponse>) rpcClient.sendRequest(rpcRequest);
            rpcResponse = responseFuture.get();
        } else if (rpcClient instanceof SocketRpcClient) {
            rpcResponse =  ((RpcResponse)rpcClient.sendRequest(rpcRequest));
        }
        ResponseChecker.check(rpcRequest, rpcResponse);
        return rpcResponse.getData();
    }
}
